//
// Created by LiuYou on 2021/5/6.
//

//#define _GNU_SOURCE  1


#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cassert>
#include <unistd.h>
#include <sys/poll.h>
#include <fcntl.h>


auto main(int argc, char** argv) -> int {
    if (argc < 3) {
        std::fprintf(stderr, "usage: %s IP Port\n", basename(argv[0]));
        return -1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    sockaddr_in serverSockAddrIn{
            .sin_family=PF_INET,
            .sin_port=htons(port),
            {.s_addr=0}
    };
    inet_pton(AF_INET, ip, &serverSockAddrIn.sin_addr);

    int socketFD = socket(PF_INET, SOCK_STREAM, 0);
    assert(socketFD >= 0);

    if (connect(socketFD, (sockaddr * ) & serverSockAddrIn, sizeof(serverSockAddrIn)) < 0) {
        std::fprintf(stderr, "connection failed\n");
        close(socketFD);
        return -1;
    }

    // 注册FD 0(stdin)和FD sockFD上的可读事件
    pollfd pollFDS[2]{{.fd=0, .events=POLLIN, .revents=0},
                      {.fd=socketFD, .events=POLLIN | POLLRDHUP, .revents=0}
    };

    const int BUFFER_SIZE = 64;
//    char readBuf[BUFFER_SIZE]{};
    char readBuf[BUFFER_SIZE] = "";
    int pipeFD[2];
    int ret = pipe(pipeFD);
    assert(ret != -1);

    for (;;) {
        ret = poll(pollFDS, sizeof(pollFDS), -1);
        if (ret < 0) {
            std::fprintf(stderr, "poll failure\n");
            break;
        }

        // 先接受
        if (pollFDS[1].revents & POLLRDHUP) {
            std::fprintf(stdout, "server close the connection\n");
            break;
        } else if (pollFDS[1].revents & POLLIN) {
            std::memset(readBuf, '\0', sizeof(readBuf));
            // TODO 这里len-1, 我不知道为什么?
            recv(pollFDS[1].fd, readBuf, sizeof(readBuf) - 1, 0);
            std::fprintf(stdout, "接受到的内容为: %s", readBuf);
        }
        // 后发送
        if (pollFDS[0].revents & POLLIN) {
            // 使用 splice 将用户输入的数据直接写到 sockFD 上(零拷贝), 提高效率。
            // TODO 32768不知道是什么意思?
            // TODO 注意: 另外需要注意要使用 splice函数必须在所有头文件之前  #define _GNU_SOURCE 1 一下。
            splice(0, NULL, pipeFD[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
            splice(pipeFD[0], NULL, socketFD, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
        }
    }

    close(socketFD);


    return 0;
}












































////
//// Created by LiuYou on 2021/5/6.
////
//
//#define _GNU_SOURCE  1
//
//
//#include <cstdio>
//#include <cstring>
//#include <cstdlib>
//#include <netinet/in.h>
//#include <arpa/inet.h>
//#include <cassert>
//#include <unistd.h>
//#include <sys/poll.h>
//#include <fcntl.h>
//
//
///**
// * @brief
// * 进行了腾讯云的内网, 不行在服务器上跑Server端识别不了Client。
// *
// *
//====================[ Build | MultiplexingIOTest01-Client | Debug ]=============
///usr/bin/cmake --build /home/lucas/CLionProjects/HighPerformanceLinuxServerProgramming-Linux/cmake-build-debug --target MultiplexingIOTest01-Client -- -j 6
//[ 50%] Linking CXX executable MultiplexingIOTest01-Client
///usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o: In function `_start':
//(.text+0x24): undefined reference to `main'
//collect2: error: ld returned 1 exit status
//gmake[3]: *** [CMakeFiles/MultiplexingIOTest01-Client.dir/build.make:84: MultiplexingIOTest01-Client] Error 1
//gmake[2]: *** [CMakeFiles/Makefile2:142: CMakeFiles/MultiplexingIOTest01-Client.dir/all] Error 2
//gmake[1]: *** [CMakeFiles/Makefile2:154: CMakeFiles/MultiplexingIOTest01-Client.dir/rule] Error 2
//gmake: *** [Makefile:144: MultiplexingIOTest01-Client] Error 2
// *
// * 真的离谱, 竟然有注释就通不过编译了, TODO 卧槽！
// * TODO 注意: 不对, 而是 #define _GNU_SOURCE  1 。这个宏定义一定一定一定一定一定要下一行就是#include指令！！！！
// *
// *
// * TODO 注意: 也不是上面的问题！！！！ 卧槽, 我心态崩了现在。
// */
//auto main(int argc, char** argv) -> int {
//    if (argc < 3) {
//        std::fprintf(stderr, "usage: %s IP Port\n", basename(argv[0]));
//        return -1;
//    }
//    const char* ip = argv[1];
//    int port = atoi(argv[2]);
//
//    sockaddr_in serverSockAddrIn{
//            .sin_family=PF_INET,
//            .sin_port=htons(port),
//            {.s_addr=0}
//    };
//    inet_pton(AF_INET, ip, &serverSockAddrIn.sin_addr);
//
//    int socketFD = socket(PF_INET, SOCK_STREAM, 0);
//    assert(socketFD >= 0);
//
//    if (connect(socketFD, (sockaddr*) &serverSockAddrIn, sizeof(serverSockAddrIn)) < 0) {
//        std::fprintf(stderr, "connection failed\n");
//        close(socketFD);
//        return -1;
//    }
//
//    // 注册FD 0(stdin)和FD sockFD上的可读事件
//    pollfd pollFDS[2]{{.fd=0, .events=POLLIN, .revents=0},
//                      {.fd=socketFD, .events=POLLIN | POLLRDHUP, .revents=0}
//    };
//
//    const int BUFFER_SIZE = 64;
////    char readBuf[BUFFER_SIZE]{};
//    char readBuf[BUFFER_SIZE] = "";
//    int pipeFD[2];
//    int ret = pipe(pipeFD);
//    assert(ret != -1);
//
//    for (;;) {
//        ret = poll(pollFDS, sizeof(pollFDS), -1);
//        if (ret < 0) {
//            std::fprintf(stderr, "poll failure\n");
//            break;
//        }
//
//        // 先接受
//        if (pollFDS[1].revents & POLLRDHUP) {
//            std::fprintf(stdout, "server close the connection\n");
//            break;
//        } else if (pollFDS[1].revents & POLLIN) {
//            std::memset(readBuf, '\0', sizeof(readBuf));
//            // TODO 这里len-1, 我不知道为什么?
//            recv(pollFDS[1].fd, readBuf, sizeof(readBuf) - 1, 0);
//            std::fprintf(stdout, "接受到的内容为: %s", readBuf);
//        }
//        // 后发送
//        if (pollFDS[0].revents & POLLIN) {
//            // 使用 splice 将用户输入的数据直接写到 sockFD 上(零拷贝), 提高效率。
//            // TODO 32768不知道是什么意思?
//            // TODO 注意: 另外需要注意要使用 splice函数必须在所有头文件之前  #define _GNU_SOURCE 1 一下。
//            splice(0, NULL, pipeFD[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
//            splice(pipeFD[0], NULL, socketFD, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
//        }
//    }
//
//    close(socketFD);
//
//
//    return 0;
//}